package dao

import (
	"context"
	"errors"
	"fmt"
	"reflect"
	"strings"
	"time"

	"github.com/go-xorm/xorm"
	"github.com/olivere/elastic/v7"

	"im/common"
	"im/dao/es"
	"im/errcode"
	"im/model"
	"im/requests"
	"im/service/orm"
)

const IndexNameOfGroups = "groups"
const GroupsRecommendDistance = "50km"
const FilterDistance = false

type GroupsDao interface {
	DeleteById(id int64, userId int64) error
	Create(user model.Users, creatable model.GroupsCreatable) (model.Groups, error)
	UpdateById(id int64, creatable model.GroupsCreatable) error
	GetById(groupId int64) (model.Groups, error)
	GetRecommendedGroups(location string, joinedGroupIds []int64, page requests.Pagination) ([]model.Groups, error)
}

func NewGroupsDaoWithEs() GroupsDao {
	return groupsDaoWithEs{dbHandle: NewGroupsDao()}
}
func NewGroupsDao() GroupsDao {
	return groupsDao{db: orm.GetDB()}
}


type groupsDaoWithEs struct {
	dbHandle GroupsDao
}

func (g groupsDaoWithEs) DeleteById(id int64, userId int64) error {
	var err error
	err = g.dbHandle.DeleteById(id, userId)
	if err != nil {
		return err
	}
	err = es.New().Update(IndexNameOfGroups, id, map[string]interface{}{
		"status": model.Groups_Status_Delete,
	})
	return err
}
func (g groupsDaoWithEs) GetRecommendedGroups(location string, joinedGroupIds []int64, page requests.Pagination) ([]model.Groups, error) {
	var groups []model.Groups
	geoPointArr := strings.Split(location, ",")
	if len(geoPointArr) != 2 {
		return groups, errcode.NewError(errcode.RequestParamInvalid, errors.New(location))
	}
	geoPoint := elastic.GeoPoint{
		Lat: common.TransferFloat(geoPointArr[0]),
		Lon: common.TransferFloat(geoPointArr[1]),
	}
	client, err := es.New().GetClient()
	if err != nil {
		return groups, err
	}
	// todo 暂时不做距离的筛选
	var query= elastic.NewBoolQuery()
	if FilterDistance{
		query = query.Filter(elastic.NewGeoDistanceQuery("location").
			Distance(GroupsRecommendDistance).GeoPoint(&geoPoint))
	}
	query = query.MustNot(elastic.NewTermsQuery("id", common.ToInterfaceSlice(joinedGroupIds)...),
		elastic.NewTermsQuery("status", model.Groups_Status_Delete))
	resp, err := client.Search().Index(es.WithPrefix(IndexNameOfGroups)).Query(query).Size(page.Limit).From(page.GetOffset()).Do(context.Background())
	if err != nil {
		return groups, errcode.NewError(errcode.EsError, err)
	}
	for _, item := range resp.Each(reflect.TypeOf(EsGroupModel{})) {
		em := item.(EsGroupModel)
		m := em.Groups
		m.Created = common.Time(em.Created)
		m.Updated = common.Time(em.Updated)
		groups = append(groups, m)
	}
	return groups, nil
}

type EsGroupModel struct {
	model.Groups
	model.TimeModelOfEs
}

func (g groupsDaoWithEs) Create(user model.Users, creatable model.GroupsCreatable) (model.Groups, error) {
	group, err := g.dbHandle.Create(user, creatable)
	if err != nil {
		return group, err
	}
	esM := EsGroupModel{
		Groups: group,
		TimeModelOfEs: model.TimeModelOfEs{
			time.Time(group.TimeModel.Created),
			time.Time(group.TimeModel.Updated),
		},
	}
	err = es.New().IndexDoc(IndexNameOfGroups, group.Id, esM)
	return group, err
}

func (g groupsDaoWithEs) UpdateById(id int64, creatable model.GroupsCreatable) error {
	err := g.dbHandle.UpdateById(id, creatable)
	if err != nil {
		return err
	}
	group, err := g.dbHandle.GetById(id)
	if err != nil {
		return err
	}
	groupEs := EsGroupModel{
		Groups: group,
		TimeModelOfEs: model.TimeModelOfEs{
			time.Time(group.TimeModel.Created),
			time.Time(group.TimeModel.Updated),
		},
	}
	return es.New().IndexDoc(IndexNameOfGroups, id, groupEs)
}

func (g groupsDaoWithEs) GetById(groupId int64) (model.Groups, error) {
	return g.dbHandle.GetById(groupId)
}

type groupsDao struct {
	db *xorm.Engine
}

func (d groupsDao) GetRecommendedGroups(location string, joinedGroupIds []int64, page requests.Pagination) ([]model.Groups, error) {
	panic("implement me")
}

func (d groupsDao) DeleteById(id int64, userId int64) error {
	var m = model.Groups{}
	m.Status = model.Groups_Status_Delete
	effected, err := d.db.Cols("status").Where("id=?", id).Where("user_id=?", userId).Update(m)
	if err != nil {
		return errcode.NewError(errcode.DaoFailed, err)
	}
	if effected < 1 {
		return errcode.NewError(errcode.DaoFailed, errors.New("删除群组失败"))
	}
	return nil
}

func (d groupsDao) Create(user model.Users, creatable model.GroupsCreatable) (model.Groups, error) {
	var group = model.Groups{
		GroupsCreatable: creatable,
		AppsId:          user.AppsId,
		UserId:          user.Id,
		Status:          model.Groups_Status_Normal,
		Approval:        model.Groups_Approval_Open,
		MaxUsers:        3000,
	}
	_, err := d.db.InsertOne(&group)
	if err != nil {
		return group, errcode.NewError(errcode.DaoFailed, err)
	}
	return group, nil
}
func (d groupsDao) UpdateById(id int64, creatable model.GroupsCreatable) error {
	group := model.Groups{
		GroupsCreatable: creatable,
	}
	_, err := d.db.Where("id=?", id).Update(group)
	return err
}

func (d groupsDao) GetById(groupId int64) (model.Groups, error) {
	var row model.Groups
	exist, err := d.db.Where("id=?", groupId).Get(&row)
	if err != nil {
		return row, errcode.NewError(errcode.DaoFailed, err)
	}
	if !exist {
		return row, errcode.NewError(errcode.DaoFailed, fmt.Errorf("not exist"))
	}
	return row, nil
}
func (d groupsDao) GetList(pagination requests.Pagination) ([]model.Groups, int64, error) {
	var rows []model.Groups
	var count int64
	var err error
	count, err = d.db.Limit(pagination.Limit, pagination.GetOffset()).FindAndCount(&rows)
	if err != nil {
		err = errcode.NewError(errcode.DaoFailed, err)
	}
	return rows, count, err
}
